/*
 * A home-made find utility demonstration.
 * author: kavinyao <kavinyao@gmail.com>
 * This program is provided as is, without any warranty.
 */

#include "common.h"
#include "predicate.h"
#include "parser.h"
#include "traverse.h"
#include "action.h"

/**
 * Simple linked list structure of directory items.
 */
typedef struct dir_list {
    char *dir_name;
    struct dir_list *next;
} dir_list;

/**
 * Sanitize directory name
 * a new copy is made and trailing slash is removed.
 * NOTE: it is the caller's responsibility to free allocated memory.
 */
char *sanitize_name(char *dir_name)
{
    size_t len = strlen(dir_name);
    char *dir = malloc(len + 1);
    if(dir) {
        strcpy(dir, dir_name);
        if('/' == dir[len-1])
            dir[len-1] = '\0';
    }

    return dir;
}

/**
 * Linked list builder.
 */
dir_list *link_dir_list(char *dir_name, dir_list *next)
{
    dir_list *dl = (dir_list *)malloc(sizeof(dir_list));
    if(dl) {
        dl->dir_name = sanitize_name(dir_name);
        dl->next = next;
    } else {
        /* make sure existing list is not lost. */
        return next;
    }

    return dl;
}

/**
 * Free allocated memory of dir_list.
 */
void destroy_dir_list(dir_list *list)
{
    dir_list *next;
    while(NULL != list) {
        next = list->next;
        free(list->dir_name);
        free(list);
        list = next;
    }
}

int main(int argc, char *argv[])
{
    /* this will keep the index of argument during parsing process */
    int arg_index = 1;

    /* Step 0: parse symlink options */
    enum SYMLINK_OPTION symlink_option = parse_sym_option(argv, &arg_index, argc);

    /* Step 1: prepare directory list */
    dir_list *dirs = NULL;
    while(arg_index < argc) {
        if('-' == argv[arg_index][0] || 
                '(' == argv[arg_index][0] ||
                '!' == argv[arg_index][0]) {
            break;
        }
        /* this argument is not a test tag, i.e. directory name */
        dirs = link_dir_list(argv[arg_index], dirs);
        arg_index++;
    }

    /* Step 2: prepare options */
    int min_depth = -1;
    int max_depth = INT_MAX;
    while(arg_index < argc) {
        if(strcmp("-version", argv[arg_index]) == 0) {
            printf("find 0.9.23\n"
                    "There is NO WARRANTY, to the extent permitted by law.\n"
                    "Written by Kavin Yao <kavinyao@gmail.com>.\n");
            destroy_dir_list(dirs);
            return 0;
        } else if(strcmp("-help", argv[arg_index]) == 0) {
            printf("%s [dirs] [options] [expressions]\n", argv[0]);
            destroy_dir_list(dirs);
            return 0;
        } else if(strcmp("-mindepth", argv[arg_index]) == 0) {
            arg_index++;
            if(arg_index == argc) {
                fprintf(stderr, "ERROR: min_depth must be provided a value\n");
                exit(1);
            }
            min_depth = atoi(argv[arg_index]);
        } else if(strcmp("-maxdepth", argv[arg_index]) == 0) {
            arg_index++;
            if(arg_index == argc) {
                fprintf(stderr, "ERROR: max_depth must be provided a value\n");
                exit(1);
            }
            max_depth = atoi(argv[arg_index]);
        } else {
            break;
        }

        arg_index++;
    }

    /* Step 3: prepare predicates */
    predicate *pred = parse_tests(argv, &arg_index, argc);

    /* Step 4: get action */
    action _action = NULL;
    if(arg_index < argc) {
        char **args = argv;
        _action = get_action(argv[arg_index], args+arg_index+1);
    }

    /* set default */
    if(NULL == _action)
        _action = print_action;

    /* Step 5: traverse */
    dir_list *dir = dirs;
    while(NULL != dir) {
        traverse(dir->dir_name, pred, _action, min_depth, max_depth, symlink_option);
        dir = dir->next;
    }

    /* Step 6: free memory */
    destroy_dir_list(dirs);
    pred->destroy(pred);

    return 0;
}

